package br.pucrio.inf.lac.konoha.hyuga.listeners;

import java.util.ArrayDeque;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;

import br.pucrio.inf.lac.konoha.hyuga.core.DDSLink;
import br.pucrio.inf.lac.konoha.hyuga.events.LocationUpdateEvent;
import br.pucrio.inf.lac.konoha.hyuga.events.cell.CellClusterEvent2;
import br.pucrio.inf.lac.konoha.hyuga.events.grid.GridClusterEvent;

import com.espertech.esper.client.EventBean;
import com.espertech.esper.client.UpdateListener;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class ClusterUpdateListener implements UpdateListener {

	/** DDSLink (Data Stream). */
	private DDSLink ddsLink;

	// JSON Mapper
	ObjectMapper mapper;

	/** LOGGER. */
	private static Logger LOGGER = Logger.getLogger(ClusterUpdateListener.class.getCanonicalName());
	
	/**
	 * Default Constructor. Receive the DDSLink to send updates to management apps.
	 * @param ddsLink - DDSLink
	 */
	public ClusterUpdateListener(DDSLink ddsLink) {
		this.ddsLink = ddsLink;
		this.mapper  = new ObjectMapper();
	}

	@Override
	public void update(EventBean[] newEvents, EventBean[] oldEvents) {
	    // Then we are only discarding events
		if (newEvents == null) { return; }
		
		boolean shouldUpdate = true;
		
		GridClusterEvent currentCluster = (GridClusterEvent) newEvents[0].get("c");
				
		Map<String, Object> clusterMap  = new HashMap<String, Object>();
		clusterMap.put("operation", "add_cluster");

		if (newEvents != null && oldEvents != null) {
		    GridClusterEvent oldCluster = (GridClusterEvent) oldEvents[0].get("c");

			if (currentCluster.hasSameCells(oldCluster)) {
				clusterMap.clear();
				clusterMap.put("operation", "cluster_update");

//				if (currentCluster.getClusterSize() == oldCluster.getClusterSize()) { // FIXME: same size, but different elements?
//					shouldUpdate = false; 											  // Right now we treat them as the same
//				}
			}
		}

		if (shouldUpdate) {
			int nsize = 0;
			if (newEvents[0].get("neighbor") != null) {
				ArrayDeque<LocationUpdateEvent> neighbors = (ArrayDeque<LocationUpdateEvent>) newEvents[0].get("neighbor");
//				currentCluster.setNeighbors(neighbors.toArray(new LocationUpdateEvent[neighbors.size()]));
			}
			
			CellClusterEvent2[] partitions = currentCluster.getCells();
			double[] x = new double[partitions.length]; // latitude index
			double[] y = new double[partitions.length]; // longitude index
			double[] s = new double[partitions.length]; // number of nodes (size)
			int totalNumNodes = 0;

			for (int i = 0; i < partitions.length; i++) {
				x[i] = partitions[i].getX();
				y[i] = partitions[i].getY();
				s[i] = partitions[i].numNodes();
				totalNumNodes += partitions[i].numNodes();
			}
			clusterMap.put("latindex", x);
			clusterMap.put("lngindex", y);
			clusterMap.put("sizeindex", s);
			clusterMap.put("core", totalNumNodes);
			clusterMap.put("neighbors", nsize);
			clusterMap.put("total", totalNumNodes + nsize);
			
			System.out.println("Found a cluster of size: " + partitions.length);
			
			try {
				ddsLink.publishOut(mapper.writeValueAsBytes(clusterMap));
			} catch (JsonProcessingException e) {
				e.printStackTrace();
			}
			
//			Bootstrap.save(currentCluster, (LocationUpdateEvent[]) newEvents[0].get("nodes"));
		}
	}
}
